--[[
Author: Starinnia
CPR is a combo points display addon based on Funkydude's BasicComboPoints
$Date: 2011-04-16 21:28:39 +0000 (Sat, 16 Apr 2011) $
$Revision: 236 $
Project Version: 2.2.2
contact: codemaster2010 AT gmail DOT com

Copyright (c) 2007-2011 Michael J. Murray aka Lyte of Lothar(US)
All rights reserved unless otherwise explicitly stated.
]]

local ComboPointsRedux = LibStub("AceAddon-3.0"):NewAddon("ComboPointsRedux", "AceConsole-3.0")
--set some module defaults
ComboPointsRedux:SetDefaultModuleState(false)
ComboPointsRedux:SetDefaultModuleLibraries("AceConsole-3.0", "AceEvent-3.0")
ComboPointsRedux:SetDefaultModulePrototype({
	OnDisable = function(self)
		if self.graphics then self.graphics:Hide() end
		if self.text then self.text:Hide() end
		self:UnregisterAllEvents()
	end,
	OnEnable = function(self)
		local modName = self:GetName()
		if not ComboPointsRedux.db.profile.modules[modName].disableGraphics then
			if not self.graphics then
				self.graphics = ComboPointsRedux:MakeGraphicsFrame(modName, self.MAX_POINTS)
			end
			self.graphics:Show()
		end
		if not ComboPointsRedux.db.profile.modules[modName].disableText then
			if not self.text then
				self.text = ComboPointsRedux:MakeTextFrame(modName)
			end
			self.text:Show()
		end
		
		for k, v in pairs(self.events) do
			if type(k) == "number" then
				self:RegisterEvent(v)
			else
				self:RegisterEvent(k, v)
			end
		end
	end,
})

--import other necessary libs
local LSM = LibStub("LibSharedMedia-3.0")
local L = LibStub("AceLocale-3.0"):GetLocale("ComboPointsRedux")

local basepath = [[Interface\Addons\ComboPointsRedux\Icons\]]

function ComboPointsRedux:OnInitialize()
	local defaults = {
		profile = {
			modules = {
				['**'] = {
					enabled = true,
					--text options
					font = "Friz Quadrata TT",
					fontsize = 12,
					outline = "OUTLINE",
					textColor = {1, 1, 1},
					textAlpha = 1,
					textStrata = "HIGH",
					textX = nil,
					textY = nil,
					clampedText = true,
					disableText = false,
					--graphics options
					orientation = "h",
					icon = "square",
					colors = {
						['**'] = {1, 0.9, 0},
						[5] = {1, 0, 0},
					},
					graphicsAlpha = 1,
					spacing = 5,
					scale = 1,
					strata = "HIGH",
					graphicsX = nil,
					graphicsY = nil,
					clampedGraphics = true,
					disableGraphics = false,
					--screen flash alert options
					enableScreenFlash = false,
					screenFlashColor = {1, 0.55, 0.098},
					screenFlashAlpha = 0.55,
					screenFlashThreshold = 0,
					screenFlashMode = "RELAXED"
				},
				['Deadly Poison'] = {
					colors = {
						[1] = {0, 1, 0},
						[2] = {0, 1, 0},
						[3] = {0, 1, 0},
						[4] = {0, 1, 0},
						[5] = {0, 0.5, 0},
					},
				},
				['Bandits Guile'] = {
					colors = {
						[1] = {0, 1, 0},
						[2] = {1, 1, 0},
						[3] = {1, 0, 0},
					},
					disableText = true,
				},
				['Fulmination'] = {
					colors = {
						['**'] = {1, 0.9, 0},
						[6] = {1, 0, 0},
					},
				},
				['Soul Shards'] = {
					colors = {
						['**'] = {0.8, 0, 1},
					},
					icon = "Shard",
				}
			},
			locked = false,
		}
	}
	
	self.db = LibStub("AceDB-3.0"):New("CPRDB2", defaults, "Default")
	
	self.db.RegisterCallback(self, "OnProfileReset", "Reset")
	self.db.RegisterCallback(self, "OnProfileCopied", "Refresh")
	self.db.RegisterCallback(self, "OnProfileChanged", "Refresh")
	
	self:RegisterChatCommand("cpr", "OpenConfig", true, true)
	self:RegisterChatCommand("cpredux", "OpenConfig", true, true)
	
	local spy = CreateFrame("FRAME", nil, UIParent)
	spy.name = "ComboPointsRedux"
	spy.addon = "ComboPointsRedux_Options"
	spy:Hide()
	spy:SetScript("OnShow", function(self)
		--remove the dummy entry
		for i, f in ipairs(INTERFACEOPTIONS_ADDONCATEGORIES) do
			if f == self.name or f.name == self.name then
				tremove(INTERFACEOPTIONS_ADDONCATEGORIES, i)
				break
			end
		end
		self:Hide()
		
		--load the config
		LoadAddOn(self.addon)
		
		--refresh the screen
		InterfaceOptionsFrame_OpenToCategory(self.name)
	end)
	InterfaceOptions_AddCategory(spy)
end

function ComboPointsRedux:OnEnable()
	for name, module in self:IterateModules() do
		if self.db.profile.modules[name].enabled then
			module:Enable()
		end
	end
	self.flasher = self:CreateFlashFrame()
end

function ComboPointsRedux:Reset()
	for name, module in self:IterateModules() do
		if self.db.profile.modules[name].enabled then
			if module:IsEnabled() then
				module:Disable()
				module:Enable()
			else
				module:Enable()
			end
		end
		
		local db = self.db.profile.modules[name]
		module.text:ClearAllPoints()
		module.graphics:ClearAllPoints()
		module.text:SetPoint("CENTER", UIParent, "CENTER", math.random(-150, 150), math.random(-150, 150))
		module.graphics:SetPoint("CENTER", UIParent, "CENTER", math.random(-150, 150), math.random(-150, 150))
		
		module.graphics:SetWidth(145*db.scale)
		module.graphics:SetHeight(25*db.scale)
		
		for i = 1, module.MAX_POINTS do
			module.graphics.points[i].icon:SetTexture(basepath..db.icon)
			module.graphics.points[i]:SetWidth(25*db.scale)
			module.graphics.points[i]:SetHeight(25*db.scale)
			module.graphics.points[i]:SetAlpha(db.graphicsAlpha)
			module.graphics.points[i]:ClearAllPoints()
		end
		
		local offset = db.spacing*db.scale
		module.graphics.points[1]:SetPoint("LEFT", module.graphics, "LEFT", 0, 0)
		for i = 2, module.MAX_POINTS do
			module.graphics.points[i]:SetPoint("LEFT", module.graphics.points[i-1], "RIGHT", offset, 0)
		end
		
		module.text.count:SetShadowColor(0, 0, 0, db.textAlpha)
		module.text.count:SetFont(LSM:Fetch("font", db.font), db.fontsize, db.outline)
		module.text.count:SetTextColor(unpack(db.textColor))
		module.text.count:SetAlpha(db.textAlpha)
		module.text:SetFrameStrata(db.textStrata)
		
		module.graphics:SetBackdropColor(1,1,1,1)
		module.graphics:EnableMouse(true)
		module.graphics:SetMovable(true)
		module.graphics.points[1]:Show()
		module.graphics.points[2]:Show()
		
		module.text:SetBackdropColor(1,1,1,1)
		module.text:EnableMouse(true)
		module.text:SetMovable(true)
		module.text:SetNumPoints(module.abbrev)
	end
end

function ComboPointsRedux:Refresh()
	for name, module in self:IterateModules() do
		--enable any modules that were disabled on the previous profile
		if self.db.profile.modules[name].enabled then
			if module:IsEnabled() then
				module:Disable()
				module:Enable()
			else
				module:Enable()
			end
		end
		
		--now update the settings of any enabled modules
		if module:IsEnabled() then
			--update will refresh all settings but the frame positions
			self:UpdateSettings(name, module)
			
			--adjust frame positions
			local db = self.db.profile.modules[name]
			
			if module.text then
				module.text:ClearAllPoints()
				
				local x = db.textX
				local y = db.textY
				if x and y then
					local s = module.text:GetEffectiveScale()
					module.text:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", x / s, y / s)
				else
					module.text:SetPoint("CENTER", UIParent, "CENTER", math.random(-150, 150), math.random(-150, 150))
				end
			end
			
			if module.graphics then
				module.graphics:ClearAllPoints()
				
				local x = db.graphicsX
				local y = db.graphicsY
				if x and y then
					local s = module.graphics:GetEffectiveScale()
					module.graphics:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", x / s, y / s)
				else
					module.graphics:SetPoint("CENTER", UIParent, "CENTER", math.random(-150, 150), math.random(-150, 150))
				end
			end
			
			if self.db.profile.locked then
				if module.text then
					module.text:EnableMouse(false)
					module.text:SetMovable(false)
					module.text:SetBackdropColor(1,1,1,0)
					module.text:SetNumPoints("")
				end
				
				if module.graphics then
					module.graphics:SetBackdropColor(1,1,1,0)
					module.graphics:EnableMouse(false)
					module.graphics:SetMovable(false)
					module.graphics.points[1]:Hide()
					module.graphics.points[2]:Hide()
				end
			else
				if module.text then
					module.text:SetBackdropColor(1,1,1,1)
					module.text:EnableMouse(true)
					module.text:SetMovable(true)
					module.text:SetNumPoints(module.abbrev)
				end
				
				if module.graphics then
					module.graphics:SetBackdropColor(1,1,1,1)
					module.graphics:EnableMouse(true)
					module.graphics:SetMovable(true)
					module.graphics.points[1]:Show()
					module.graphics.points[2]:Show()
				end
			end
		end
	end
end

function ComboPointsRedux:UpdatePositions(name)
	local db = self.db.profile.modules[name]
	local module = self:GetModule(name)
	
	if module.text then
		module.text:ClearAllPoints()
		
		local x = db.textX
		local y = db.textY
		local s = module.text:GetEffectiveScale()
		module.text:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", x / s, y / s)
	end
	
	if module.graphics then
		module.graphics:ClearAllPoints()
		
		local x = db.graphicsX or 0
		local y = db.graphicsY or 0
		local s = module.graphics:GetEffectiveScale()
		module.graphics:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", x / s, y / s)
	end
end

function ComboPointsRedux:UpdateSettings(name)
	local db = self.db.profile.modules[name]
	local module = self:GetModule(name)
	local num = module.MAX_POINTS
	
	--graphics
	if not db.disableGraphics then
		--change icon textures
		--update icon Alpha
		for i = 1, module.MAX_POINTS do
			module.graphics.points[i].icon:SetTexture(basepath..db.icon)
			module.graphics.points[i]:SetAlpha(db.graphicsAlpha)
			module.graphics.points[i]:SetWidth(25*db.scale)
			module.graphics.points[i]:SetHeight(25*db.scale)
			module.graphics.points[i]:ClearAllPoints()
		end
		
		--adjust container scale
		if db.orientation == "v" then
			module.graphics:SetHeight(((25 * num) + (5 * (num - 1))) * db.scale)
			module.graphics:SetWidth(25*db.scale)
		else
			module.graphics:SetWidth(((25 * num) + (5 * (num - 1))) * db.scale)
			module.graphics:SetHeight(25*db.scale)
		end
		
		--adjust for orientation changes (this updates spacing too)
		local offset = db.spacing*db.scale
		if db.orientation == "v" then
			module.graphics.points[1]:SetPoint("BOTTOM", module.graphics, "BOTTOM", 0, 0)
			for i = 2, module.MAX_POINTS do
				module.graphics.points[i]:SetPoint("BOTTOM", module.graphics.points[i-1], "TOP", 0, offset)
			end
		else
			module.graphics.points[1]:SetPoint("LEFT", module.graphics, "LEFT", 0, 0)
			for i = 2, module.MAX_POINTS do
				module.graphics.points[i]:SetPoint("LEFT", module.graphics.points[i-1], "RIGHT", offset, 0)
			end
		end
		
		--update strata
		module.graphics:SetFrameStrata(db.strata)
	end
	
	--text
	if not db.disableText then
		--update font, font size, alpha and strata
		module.text.count:SetShadowColor(0, 0, 0, db.textAlpha)
		module.text.count:SetFont(LSM:Fetch("font", db.font), db.fontsize, db.outline)
		module.text.count:SetTextColor(unpack(db.textColor))
		module.text.count:SetAlpha(db.textAlpha)
		module.text:SetFrameStrata(db.textStrata)
	end
end

function ComboPointsRedux:OpenConfig()
	LoadAddOn("ComboPointsRedux_Options")
	LibStub("AceConfigDialog-3.0"):Open("ComboPointsRedux")
end

function ComboPointsRedux:GetColorByPoints(module, points)
	return unpack(self.db.profile.modules[module].colors[points])
end

local function OnDragStart(frame)
	frame:StartMoving()
end

local function OnDragStop(frame)
	frame:StopMovingOrSizing()
	local dba = ComboPointsRedux.db.profile.modules[frame.moduleName]
	local s = frame:GetEffectiveScale()
	dba[frame.moduleType.."X"] = frame:GetLeft() * s
	dba[frame.moduleType.."Y"] = frame:GetTop() * s
end

local function OnLeave(frame)
	GameTooltip:Hide()
end

local function OnEnter(frame)
	if ComboPointsRedux.db.profile.locked then return end
	
	GameTooltip:SetOwner(frame)
	GameTooltip:ClearLines()
	if frame.moduleType == "text" then
		GameTooltip:AddLine(format(L["%s Text"], frame.moduleName))
	else
		GameTooltip:AddLine(format(L["%s Graphics"], frame.moduleName))
	end
	GameTooltip:AddLine(L["Left click and drag to move the frame."])
	GameTooltip:AddLine(L["To hide the background, open the options and select the 'Lock' option."])
	GameTooltip:Show()
end

local bg = {bgFile = "Interface\\Tooltips\\UI-Tooltip-Background"}
function ComboPointsRedux:MakeTextFrame(moduleName)
	local f = CreateFrame("FRAME", nil, UIParent)
	f.moduleName = moduleName
	f.moduleType = "text"
	
	f:SetWidth(50)
	f:SetHeight(50)
	
	local db = self.db.profile.modules[moduleName]
	f:SetBackdrop(bg)
	f:SetFrameStrata(db.textStrata)
	f:SetClampedToScreen(db.clampedText)
	
	local x = db.textX
	local y = db.textY
	local s = f:GetEffectiveScale()
	if x and y then
		f:ClearAllPoints()
		f:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", x / s, y / s)
	else
		f:ClearAllPoints()
		f:SetPoint("CENTER", UIParent, "CENTER", math.random(-150, 150), math.random(-150, 150))
		db.textX = f:GetLeft() * s
		db.textY = f:GetTop() * s
	end
	
	f:SetBackdropColor(1,1,1,0)
	
	f.count = f:CreateFontString(nil,"OVERLAY")
	f.count:SetAllPoints(f)
	f.count:SetShadowColor(0, 0, 0, db.textAlpha)
	f.count:SetShadowOffset(1, -1)
	f.count:SetFont(LSM:Fetch("font", db.font), db.fontsize, db.outline)
	f.count:SetTextColor(unpack(db.textColor))
	f.count:SetAlpha(db.textAlpha)
	
	f:RegisterForDrag("LeftButton")
	f:SetScript("OnDragStart", OnDragStart)
	f:SetScript("OnDragStop", OnDragStop)
	f:SetScript("OnEnter", OnEnter)
	f:SetScript("OnLeave", OnLeave)
	
	if not self.db.profile.locked then
		f:SetBackdropColor(1,1,1,1)
		f:EnableMouse(true)
		f:SetMovable(true)
		f.count:SetText(self.modules[moduleName].abbrev)
	else
		f:EnableMouse(false)
		f:SetMovable(false)
	end
	
	function f:SetNumPoints(points)
		self.count:SetText(points)
	end
	
	f:Hide()
	
	return f
end

function ComboPointsRedux:MakeGraphicsFrame(moduleName, num)
	local g = CreateFrame("FRAME", nil, UIParent)
	g.moduleName = moduleName
	g.moduleType = "graphics"
	
	local db = self.db.profile.modules[moduleName]
	g:SetBackdrop(bg)
	g:SetFrameStrata(db.strata)
	g:SetClampedToScreen(db.clampedGraphics)
	
	if db.orientation == "v" then
		g:SetHeight(((25 * num) + (5 * (num - 1))) * db.scale)
		g:SetWidth(25*db.scale)
	else
		g:SetWidth(((25 * num) + (5 * (num - 1))) * db.scale)
		g:SetHeight(25*db.scale)
	end
	
	local x = db.graphicsX
	local y = db.graphicsY
	local s = g:GetEffectiveScale()
	if x and y then
		g:ClearAllPoints()
		g:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", x / s, y / s)
	else
		g:ClearAllPoints()
		g:SetPoint("CENTER", UIParent, "CENTER", math.random(-150, 150), math.random(-150, 150))
		db.graphicsX = g:GetLeft() * s
		db.graphicsY = g:GetTop() * s
	end
	
	g:SetBackdropColor(1,1,1,0)
	
	g:RegisterForDrag("LeftButton")
	g:SetScript("OnDragStart", OnDragStart)
	g:SetScript("OnDragStop", OnDragStop)
	g:SetScript("OnEnter", OnEnter)
	g:SetScript("OnLeave", OnLeave)
	
	g.points = {}
	for i = 1, num do
		g.points[i] = CreateFrame("FRAME", nil, g)
		g.points[i].icon = g.points[i]:CreateTexture(nil, "OVERLAY")
		g.points[i].icon:SetAllPoints(g.points[i])
		g.points[i].icon:SetTexture(basepath..db.icon)
		g.points[i].icon:SetVertexColor(unpack(db.colors[1]))
		g.points[i]:SetAlpha(db.graphicsAlpha)
		g.points[i]:SetHeight(25*db.scale)
		g.points[i]:SetWidth(25*db.scale)
		g.points[i]:Hide()
	end
	
	if not self.db.profile.locked then
		g:SetBackdropColor(1,1,1,1)
		g:EnableMouse(true)
		g:SetMovable(true)
		g.points[1]:Show()
		g.points[2]:Show()
	else
		g:EnableMouse(false)
		g:SetMovable(false)
	end
	
	local offset = db.spacing*db.scale
	if db.orientation == "v" then
		g.points[1]:SetPoint("BOTTOM", g, "BOTTOM", 0, 0)
		for i = 2, num do
			g.points[i]:SetPoint("BOTTOM", g.points[i-1], "TOP", 0, offset)
		end
	else
		g.points[1]:SetPoint("LEFT", g, "LEFT", 0, 0)
		for i = 2, num do
			g.points[i]:SetPoint("LEFT", g.points[i-1], "RIGHT", offset, 0)
		end
	end
	
	g:Hide()
	
	return g
end

function ComboPointsRedux:CreateFlashFrame()
	--only make the frame once
	if COMBOPOINTSREDUX_FLASHFRAME then
		return COMBOPOINTSREDUX_FLASHFRAME
	end
	
	--thanks to BigWigs for the flasher code
	--prolly same as what is used in Omen as well
	local flasher = CreateFrame("Frame", "COMBOPOINTSREDUX_FLASHFRAME", UIParent)
	flasher:SetFrameStrata("BACKGROUND")
	flasher:SetBackdrop({bgFile = [[Interface\Tooltips\UI-Tooltip-Background]],})
	flasher:SetAllPoints(UIParent)
	flasher:SetScript("OnShow", function(frame)
		frame.elapsed = 0
		frame:SetAlpha(0)
	end)
	flasher:SetScript("OnUpdate", function(frame, elapsed)
		elapsed = frame.elapsed + elapsed
		if elapsed >= 0.8 then
			frame:Hide()
			frame:SetAlpha(0)
			return
		end
		local alpha = elapsed % 0.4
		if elapsed > 0.2 then
			alpha = 0.4 - alpha
		end
		frame:SetAlpha(alpha * 5)
		frame.elapsed = elapsed
	end)
	flasher:Hide()
	
	return flasher
end

function ComboPointsRedux:DoFlash(modName, count)
	local db = self.db.profile.modules[modName]
	--don't try to reshow the flasher if it is already going
	--if the threshold is zero the effect is disabled
	if (not db.enableScreenFlash) or self.flasher:IsShown() then return end
	
	--if flash mode is strict, only flash at the exact point value
	--if the flash mode is relaxed, flash at and above the threshold
	if (db.screenFlashMode == "STRICT" and count == db.screenFlashThreshold) or (db.screenFlashMode == "RELAXED" and count >= db.screenFlashThreshold) then
		local r, g, b = unpack(db.screenFlashColor)
		self.flasher:SetBackdropColor(r, g, b, db.screenFlashAlpha)
		self.flasher:Show()
	end
end
